package com.example.library;


import com.example.library.util.AttrUtils;
import com.example.library.util.LogUtil;
import com.example.library.vPage.BezierViewPager;
import com.example.library.vPage.CardPagerAdapter;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.PageSlider;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;
import ohos.utils.geo.Point;

import java.util.Arrays;
import java.util.Optional;


public class BezierRoundView extends Component implements PageSlider.PageChangedListener,
    Component.TouchEventListener, Component.DrawTask, Component.EstimateSizeListener {
  public BezierRoundView(Context context) {
    super(context, null);
  }

  public BezierRoundView(Context context, AttrSet attrs) {
    super(context, attrs, 0);

    mRadius = AttrHelper.vp2px(15, context);
    color_bez = AttrUtils.getAttrSetColor(attrs, "color_bez", new Color(color_bez));
    color_touch = AttrUtils.getAttrSetColor(attrs, "color_touch", new Color(color_touch));
    color_stroke = AttrUtils.getAttrSetColor(attrs, "color_stroke", new Color(color_stroke));
    time_animator = AttrUtils.getInt(attrs, "time_animator", time_animator);
    default_round_count = AttrUtils.getInt(attrs, "default_round_count", default_round_count);
    mRadius = AttrUtils.getDimension(attrs, "radius", mRadius);

    setTouchEventListener(this::onTouchEvent);
    setEstimateSizeListener(this::onEstimateSize);
    addDrawTask(this::onDraw);
    init();
  }

  public BezierRoundView(Context context, AttrSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    mRadius = AttrHelper.vp2px(15, context);
    color_bez = AttrUtils.getAttrSetColor(attrs, "color_bez", new Color(color_bez));
    color_touch = AttrUtils.getAttrSetColor(attrs, "color_touch", new Color(color_touch));
    color_stroke = AttrUtils.getAttrSetColor(attrs, "color_stroke", new Color(color_stroke));
    time_animator = AttrUtils.getInt(attrs, "time_animator", time_animator);
    default_round_count = AttrUtils.getInt(attrs, "default_round_count", default_round_count);
    mRadius = AttrUtils.getDimension(attrs, "radius", mRadius);

    setTouchEventListener(this::onTouchEvent);
    setEstimateSizeListener(this::onEstimateSize);
    addDrawTask(this::onDraw);
    init();
  }

  private final String TAG = "QDX";
  private int time_animator = 600;  //动画时间
  private Matrix matrix_bounceL;   //将向右弹的动画改为向左
  private int color_bez = 0xfffe626d;
  private int color_touch = 0xfffe626d;
  private int color_stroke = Color.GRAY.getValue();

  private void init() {

    DEFAULT_HEIGHT = mRadius * 3;
    mBezPaint = new Paint();
    mBezPaint.setAntiAlias(true);
    mBezPaint.setColor(new Color(color_bez)); //默认QQ糖的颜色为粉红色
    mBezPaint.setStyle(Paint.Style.FILL_STYLE);

    mRoundStrokePaint = new Paint();
    mBezPaint.setAntiAlias(true);
    mRoundStrokePaint.setColor(new Color(color_stroke));
    mRoundStrokePaint.setStyle(Paint.Style.STROKE_STYLE);
    mRoundStrokePaint.setStrokeWidth(2);

    mTouchPaint = new Paint();
    mBezPaint.setAntiAlias(true);
    mTouchPaint.setColor(new Color(color_touch)); //默认触摸反馈颜色也为粉红色
    mTouchPaint.setStyle(Paint.Style.FILL_STYLE);
    mTouchPaint.setBlendMode(BlendMode.CLEAR);
    mBezPath = new Path();

    //y轴一样
    p5 = new Point(mRadius * bezFactor, mRadius);
    p6 = new Point(0, mRadius);
    p7 = new Point(-mRadius * bezFactor, mRadius);
    //y轴一样
    p0 = new Point(0, -mRadius);
    p1 = new Point(mRadius * bezFactor, -mRadius);
    p11 = new Point(-mRadius * bezFactor, -mRadius);
    //x轴一样
    p2 = new Point(mRadius, -mRadius * bezFactor);
    p3 = new Point(mRadius, 0);
    p4 = new Point(mRadius, mRadius * bezFactor);
    //x轴一样
    p8 = new Point(-mRadius, mRadius * bezFactor);
    p9 = new Point(-mRadius, 0);
    p10 = new Point(-mRadius, -mRadius * bezFactor);

    matrix_bounceL = new Matrix();
    matrix_bounceL.preScale(-1, 1);
  }

  private int DEFAULT_WIDTH;
  private int DEFAULT_HEIGHT;
  private int default_round_count = 4;   //默认圆球的数量


  /**
   * 测绘measure
   *
   * @param specType    1为宽， 其他为高
   * @param contentSize 默认值
   */
  private int measureSize(int specType, int contentSize, int measureSpec) {
    int result;
    //获取测量的模式和Size
    int specMode = EstimateSpec.getMode(measureSpec);
    int specSize = EstimateSpec.getSize(measureSpec);

    if (specMode == EstimateSpec.PRECISE) {
      result = Math.min(contentSize, specSize);
    } else {
      result = contentSize;

      if (specType == 1) {
        // 根据传人方式计算宽
        result += (getPaddingLeft() + getPaddingRight());
      } else {
        // 根据传人方式计算高
        result += (getPaddingTop() + getPaddingBottom());
      }
    }

    return result;
  }

  private Paint mBezPaint;
  private Paint mRoundStrokePaint;
  private Paint mTouchPaint;
  private int mWidth;
  private int mHeight;
  private int mRadius;
  private final float bezFactor = 0.551915024494f;

  private Path mBezPath;

  private Point p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11;


  private float rRadio = 1;  //P2,3,4 x轴倍数
  private float lRadio = 1;  //P8,9,10倍数
  private float tbRadio = 1;  //y轴缩放倍数
  private float boundRadio = 0.55f;  //进入另一个圆的回弹效果

  /**
   * 离开圆的阈值
   */
  private float disL = 0.5f;
  /**
   * 最大值的阈值
   */
  private float disM = 0.8f;
  /**
   * 到达下个圆的阈值
   */
  private float disA = 0.9f;


  private float[] bezPos; //记录每一个圆心x轴的位置
  private float[] xPivotPos;  //根据圆心x轴+mRadius，划分成不同的区域 ,主要为了判断触摸x轴的位置
  private int curPos = 0;  //当前圆的位置
  private int nextPos = 0; //圆要到达的下一个位置


  private BezierViewPager mViewPage;

  /**
   * 关联ViewPager，监听scroll进行改变bezRound
   */
  public void attach2ViewPage(BezierViewPager vPage) {
    vPage.addPageChangedListener(this);
    this.mViewPage = vPage;
    if (CardPagerAdapter.class.isInstance(vPage.getProvider())) {
      this.default_round_count = vPage.getProvider().getCount();
      initCountPos();
    }
  }

  private void initCountPos() {
    bezPos = new float[default_round_count];
    xPivotPos = new float[default_round_count];
    for (int i = 0; i < default_round_count; i++) {
      bezPos[i] = mWidth / (default_round_count + 1) * (i + 1);
      xPivotPos[i] = mWidth / (default_round_count + 1) * (i + 1) + mRadius;
    }
  }


  //展示QQ糖动画
  private AnimatorValue animatorStart;
  //不确定
  //  private TimeInterpolator timeInterpolator = new DecelerateInterpolator();
  private float animatedValue;
  private boolean isAniming = false;

  public void startAnimator() {
    if (animatorStart != null) {
      if (animatorStart.isRunning()) {
        return;
      }
      animatorStart.start();
    } else {
      animatorStart = new AnimatorValue();
      animatorStart.setDuration(time_animator);
      animatorStart.setCurveType(Animator.CurveType.DECELERATE);

      animatorStart.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
          isAniming = true;
          animatedValue = v;
          invalidate();
        }
      });
      animatorStart.setStateChangedListener(new Animator.StateChangedListener() {
        @Override
        public void onStart(Animator animator) {
          isAniming = true;
          if (mViewPage != null) {
            mViewPage.setTouchable(false);
          }
        }

        @Override
        public void onStop(Animator animator) {

        }

        @Override
        public void onCancel(Animator animator) {
          isAniming = false;
          curPos = nextPos;
          if (mViewPage != null) {
            mViewPage.setTouchable(true);
          }
        }

        @Override
        public void onEnd(Animator animator) {
          isAniming = false;
          curPos = nextPos;
          if (mViewPage != null) {
            mViewPage.setTouchable(true);
          }
        }

        @Override
        public void onPause(Animator animator) {

        }

        @Override
        public void onResume(Animator animator) {

        }
      });

      animatorStart.start();
    }
  }

  private AnimatorValue animatorTouch;
  private boolean isTouchAniming = false;
  private float animatedTouchValue;
  private RectFloat rectF_touch = new RectFloat();  //触摸反馈范围
  private boolean isClick; //点击圆球

  private void startTouchAnimator() {
    //设置触摸范围
    rectF_touch.fuse(bezPos[nextPos] - mRadius * 1.5f, -mRadius * 1.5f,
        bezPos[nextPos] + mRadius * 1.5f, mRadius * 1.5f);
    isClick = true;
    if (animatorTouch != null) {
      if (animatorTouch.isRunning()) {
        return;
      }
      isTouchAniming = true;
      animatorTouch.start();
    } else {
      animatorTouch = new AnimatorValue();
      animatorTouch.setDuration(time_animator / 2);
      animatorTouch.setCurveType(Animator.CurveType.DECELERATE);
      animatorTouch.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
          animatedTouchValue = v;
          if (animatedTouchValue == 1) {
            isTouchAniming = false;
          }
        }

      });
      isTouchAniming = true;
      animatorTouch.start();
    }
  }

  /**
   * 通过 path 将向右弹射的动画绘制出来
   * 如果要绘制向左的动画，只要设置path的transform(matrix)即可
   */
  private void bounce2RightRound() {
    mBezPath.moveTo(p0.getPointX(), p0.getPointY() * tbRadio);
    mBezPath.cubicTo(p1.getPointX(), p1.getPointY() * tbRadio, p2.getPointX() * rRadio, p2.getPointY(), p3.getPointX() * rRadio, p3.getPointY());
    mBezPath.cubicTo(p4.getPointX() * rRadio, p4.getPointY(), p5.getPointX(), p5.getPointY() * tbRadio, p6.getPointX(), p6.getPointY() * tbRadio);
    mBezPath.cubicTo(p7.getPointX(), p7.getPointY() * tbRadio, p8.getPointX() * lRadio, p8.getPointY(), p9.getPointX() * lRadio, p9.getPointY());
    mBezPath.cubicTo(p10.getPointX() * lRadio, p10.getPointY(), p11.getPointX(), p11.getPointY() * tbRadio, p0.getPointX(), p0.getPointY() * tbRadio);
    mBezPath.close();
  }


  /**
   * 将animatedValue值域转化为[0,1]
   *
   * @param minValue 大于等于
   * @param maxValue 小于等于
   * @return 根据当前 animatedValue,返回 [0,1] 对应的数值
   */
  private float range0Until1(float minValue, float maxValue) {
    return (animatedValue - minValue) / (maxValue - minValue);
  }


  private boolean direction; //方向 , true是位置向右(0->1)


  public void setAnimDuration(int time) {
    time_animator = time;
  }

  /**
   * 设置圆的半径
   */
  public void setRadius(int radius) {
    this.mRadius = radius;
    init();
  }

  /**
   * 设置bez 圆的数量，默认4个
   */
  public void setRoundCount(int count) {
    this.default_round_count = count;
    initCountPos();
  }

  /**
   * 设置bez 圆的颜色，默认粉红色
   */
  public void setBezRoundColor(int roundcolor) {
    color_bez = roundcolor;
    mBezPaint.setColor(new Color(roundcolor));
  }

  /**
   * 触摸效果颜色，默认粉红色
   */
  public void setTouchColor(int touchColor) {
    color_touch = touchColor;
    mTouchPaint.setColor(new Color(touchColor));
  }

  /**
   * 圆框的颜色
   */
  public void setStrokeColor(int strokeColor) {
    color_stroke = strokeColor;
    mRoundStrokePaint.setColor(new Color(strokeColor));
  }


  private float myPositionOffset;
  private int myPosition;

  /**
   * @param position       当前cur位置，如果当前是1，手指右滑（vPage向左滑动）那就是0， 左滑至下一个位置才为2
   * @param positionOffset [0,1) ,到达下一个pos就置为0
   */
  @Override
  public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
    if (isAniming) { //如果是手动选择pos，就用animatorValue去计算 0-->3
      return;
    }

    LogUtil.warn(TAG, "onPageScrolled positionOffset==" + positionOffset + " position:" + position
        + " positionOffsetPixels :" + positionOffsetPixels);
    // animatedValue = positionOffset;
    if (positionOffsetPixels < 0) {
      myPositionOffset = (1 - positionOffset);
    } else if (positionOffsetPixels > 0) {
      myPositionOffset = positionOffset;
    } else {
      animatedValue = positionOffset;
    }
    animatedValue = myPositionOffset;

    direction = (positionOffsetPixels > 0);  //运动方向。 true为右边(手往左滑动)
    nextPos = direction ? curPos : curPos;  //右 +1   左 -1

    if (!direction) {
      //如果是向左
      animatedValue = 1 - animatedValue;  //让 animatedValue 不管是左滑还是右滑，都从[0,1)开始计算
    }

    if (myPositionOffset == 0) {
      curPos = myPosition;
      nextPos = myPosition;
    }

    //快速滑动的时候，positionOffset有可能不会置于0
    if (direction && myPosition + myPositionOffset > nextPos) {  //向右，而且
      curPos = myPosition;
      nextPos = myPosition;
    } else if (!direction && myPosition + myPositionOffset < nextPos) {
      curPos = myPosition;
      nextPos = myPosition;
    }

    LogUtil.warn(TAG, "onPageScrolled animatedValue==" + animatedValue);
    LogUtil.warn(TAG, "onPageScrolled direction==" + direction);
    LogUtil.warn(TAG, "onPageScrolled curPos==" + curPos);
    LogUtil.warn(TAG, "onPageScrolled nextPos==" + nextPos);
    LogUtil.warn(TAG, "onPageScrolled position==" + myPosition);
    invalidate();

  }

  @Override
  public void onPageSlideStateChanged(int i) {

  }

  @Override
  public void onPageChosen(int i) {
    myPosition = i;
  }


  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN:
        MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
        float x = point.getX();
        float y = point.getY();

        if (y <= mHeight / 2 + mRadius && y >= mHeight / 2 - mRadius && !isAniming) {  //先判断y，如果y点击是在圆y轴的范围
          int pos = -Arrays.binarySearch(xPivotPos, x) - 1;
          if (pos >= 0 && pos < default_round_count && x + mRadius >= bezPos[pos]) {
            nextPos = pos;

            LogUtil.error(TAG, "ontouch  curPos" + curPos);
            LogUtil.error(TAG, "ontouch  nextPos" + nextPos);
            LogUtil.error(TAG, "ontouch  isAniming" + isAniming);
            if (mViewPage != null && curPos != nextPos) {
              mViewPage.setCurrentPage(pos);
              isAniming = true;
              direction = (curPos < pos);

              startAnimator();
              startTouchAnimator();
            }
          }
          return true;
        }
        break;
    }

    //return super.TouchEvent(event);
    return false;
  }

  @Override
  public void onDraw(Component component, Canvas canvas) {
    mTouchPaint.setBlendMode(null);
    canvas.translate(0, mHeight / 2);
    mBezPath.reset();
    for (int i = 0; i < default_round_count; i++) {
      canvas.drawCircle(bezPos[i], 0, mRadius - 2, mRoundStrokePaint);   //绘制圆框
    }
    if (animatedValue == 1) {
      if (nextPos == 4) {
        return;
      }
      canvas.drawCircle(bezPos[nextPos], 0, mRadius, mBezPaint);
      return;
    }

    if (isTouchAniming) {  //实现 触摸反馈
      int count = canvas.saveLayer(rectF_touch, mTouchPaint);
      if (nextPos == 4) {
        return;
      }

      canvas.drawCircle(bezPos[nextPos], 0, 80 * animatedTouchValue, mTouchPaint);   //先画一个白色的圆 [0,mRadius*1.5]
      mTouchPaint.setBlendMode(BlendMode.CLEAR);
      canvas.drawCircle(bezPos[nextPos], 0, 70 * animatedTouchValue, mTouchPaint);  //从 0.7-1.4   效果会更好看！
      if (animatedTouchValue >= 0.7) {             //如果白色的圆半径>=mRadis ，就开始绘制透明的圆
        canvas.drawCircle(bezPos[nextPos], 0, (30 * animatedTouchValue), mTouchPaint);
      }
      mTouchPaint.setBlendMode(null);

      canvas.restoreToCount(count);
    }

    canvas.translate(bezPos[curPos], 0);

    if (0 < animatedValue && animatedValue <= disL) {
      rRadio = 1f + animatedValue * 2;                         //  [1,2]
      lRadio = 1f;
      tbRadio = 1f;
    }
    if (disL < animatedValue && animatedValue <= disM) {
      rRadio = 2 - range0Until1(disL, disM) * 0.5f;          //  [2,1.5]
      lRadio = 1 + range0Until1(disL, disM) * 0.5f;          // [1,1.5]
      tbRadio = 1 - range0Until1(disL, disM) / 3;           // [1 , 2/3]
    }
    if (disM < animatedValue && animatedValue <= disA) {
      rRadio = 1.5f - range0Until1(disM, disA) * 0.5f;     //  [1.5,1]
      lRadio = 1.5f - range0Until1(disM, disA) * (1.5f - boundRadio);      //反弹效果，进场 内弹boundRadio
      tbRadio = (range0Until1(disM, disA) + 2) / 3;        // [ 2/3,1]
    }
    if (disA < animatedValue && animatedValue <= 1f) {
      rRadio = 1;
      tbRadio = 1;
      lRadio = boundRadio + range0Until1(disA, 1) * (1 - boundRadio);     //反弹效果，饱和
    }
    if (animatedValue == 1 || animatedValue == 0) {  //防止极其粗暴的滑动
      rRadio = 1f;
      lRadio = 1f;
      tbRadio = 1f;
    }


    boolean isTrans = false;
    float transX = (nextPos - curPos) * (mWidth / (default_round_count + 1));
    if (disL <= animatedValue && animatedValue <= disA) {
      isTrans = true;

      transX = transX * (animatedValue - disL) / (disA - disL);
    }
    if (disA < animatedValue && animatedValue <= 1) {
      isTrans = true;
    }
    if (isTrans) {
      canvas.translate(transX, 0);
    }

    bounce2RightRound();

    if (!direction) {
      mBezPath.transform(matrix_bounceL);
    }
    canvas.drawPath(mBezPath, mBezPaint);

    if (isTrans) {
      canvas.save();
    }
  }

  @Override
  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    if (DEFAULT_WIDTH == 0) {
      Optional<Display>
          display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
      DisplayAttributes displayAttributes = display.get().getAttributes();
      DEFAULT_WIDTH = displayAttributes.width;
    }

    int width = measureSize(1, DEFAULT_WIDTH, widthMeasureSpec);
    int height = measureSize(1, DEFAULT_HEIGHT, heightMeasureSpec);
    setEstimatedSize(width, height);

    mWidth = getEstimatedWidth();
    mHeight = getEstimatedHeight();
    initCountPos();

    return false;
  }
}
